// NRC Language Complete Test Suite
// Tests for functions, slices, and other features

// ========================================
// Function Definitions
// ========================================

// Functions with no return value
func sayHello() {
    println("Hello, World!")
}

func printNumber(num int) {
    println("Number:", num)
}

// Functions with single return value
func getAnswer() int {
    return 42
}

func square(n int) int {
    return n * n
}

func add(a int, b int) int {
    return a + b
}

func multiply(a int, b int) int {
    return a * b
}

func subtract(a int, b int) int {
    return a - b
}

func divide(a float64, b float64) float64 {
    return a / b
}

func addFloat(a float64, b float64) float64 {
    return a + b
}

// Functions with expressions
func max(a int, b int) int {
    if (a > b) {
        return a
    }
    return b
}

func min(a int, b int) int {
    if (a < b) {
        return a
    } else {
        return b
    }
}

// Functions with reference parameters
func incrementRef(ref &int) {
    ref = ref + 1
}

func doubleRef(ref &int) int {
    return ref * 2
}

// Note: Cannot safely return reference to local variable
// Commented out as it would return dangling reference
// func createInt(value int) &int {
//     var temp int = value
//     return &temp  // UNSAFE: temp is destroyed after return
// }

func swapRef(a &int, b &int) {
    var temp int = a
    a = b
    b = temp
}

func addRef(a &int, b &int) int {
    return a + b
}

// ========================================
// Lambda and Higher-Order Functions
// ========================================

// Function that takes another function as parameter
func apply_operation(x int, y int, op func(int, int) int) int {
    op(x, y)
}

// Function that returns a lambda (multiplier)
func get_multiplier() func(int, int) int {
    func(a int, b int) int { a * b }
}

// Function that returns a lambda (divider)
func get_divider() func(int, int) int {
    func(a int, b int) int { a / b }
}

// Function that takes a unary function as parameter
func apply_unary(x int, op func(int) int) int {
    op(x)
}

// Functions with local variables
func sumSquares(a int, b int) int {
    var aSquared int = a * a
    var bSquared int = b * b
    return aSquared + bSquared
}

func factorial(n int) int {
    if (n <= 1) {
        return 1
    }
    var result int = 1
    var i int = 2
    for (i <= n) {
        result = result * i
        i = i + 1
    }
    return result
}

// ========================================
// Main Test Suite
// ========================================

func test_function_feature_suite() {
    println("========================================")
    println("NRC Language Complete Test Suite")
    println("========================================")
    println("")

    // =====================================================
    // SECTION A: Function Tests
    // =====================================================
    println("[SECTION A] Function Tests")
    println("")

    println("A1. Functions with no return value")
    sayHello()
    printNumber(42)
    println("")

    println("A2. Functions with no parameters")
    var answer int = getAnswer()
    println("    The answer to everything:", answer)
    assert(answer == 42, "getAnswer() should return 42")
    println("")

    println("A3. Single parameter functions")
    var num int = 7
    var squared int = square(num)
    println("    Square of 7:", squared)
    assert(squared == 49, "square(7) should be 49")
    println("")

    println("A4. Multiple parameter functions (int)")
    var sum int = add(15, 27)
    println("    15 + 27 =", sum)
    assert(sum == 42, "add(15, 27) should be 42")
    var product int = multiply(6, 8)
    println("    6 * 8 =", product)
    assert(product == 48, "multiply(6, 8) should be 48")
    var diff int = subtract(50, 23)
    println("    50 - 23 =", diff)
    assert(diff == 27, "subtract(50, 23) should be 27")
    println("")

    println("A5. Float functions")
    var quotient float64 = divide(10.0, 3.0)
    println("    10.0 / 3.0 =", quotient)
    var fsum float64 = addFloat(3.14, 2.86)
    println("    3.14 + 2.86 =", fsum)
    println("")

    println("A6. Functions with conditional logic")
    var maxVal int = max(42, 17)
    println("    max(42, 17) =", maxVal)
    assert(maxVal == 42, "max(42, 17) should be 42")
    var minVal int = min(42, 17)
    println("    min(42, 17) =", minVal)
    assert(minVal == 17, "min(42, 17) should be 17")
    println("")

    println("A7. Reference functions")
    var temp1 int = 10
    var refVal &int = &temp1
    println("    Initial reference value:", refVal)
    assert(refVal == 10, "Initial refVal should be 10")
    incrementRef(refVal)
    println("    After increment:", refVal)
    assert(refVal == 11, "After increment refVal should be 11")
    var doubled int = doubleRef(refVal)
    println("    Doubled value:", doubled)
    assert(doubled == 22, "doubleRef(11) should be 22")
    // Commented out: createInt is unsafe
    // var newRef &int = createInt(99)
    // println("    Created reference with value:", newRef)
    var temp2 int = 100
    var temp3 int = 200
    var refX &int = &temp2
    var refY &int = &temp3
    println("    Before swap: x =", refX, ", y =", refY)
    assert(refX == 100, "refX should be 100 before swap")
    assert(refY == 200, "refY should be 200 before swap")
    swapRef(refX, refY)
    println("    After swap: x =", refX, ", y =", refY)
    assert(refX == 200, "refX should be 200 after swap")
    assert(refY == 100, "refY should be 100 after swap")
    var sumRef int = addRef(refX, refY)
    println("    Sum via references:", sumRef)
    assert(sumRef == 300, "addRef(200, 100) should be 300")
    println("")

    println("A8. Complex functions")
    var sumSq int = sumSquares(3, 4)
    println("    sumSquares(3, 4) =", sumSq)
    assert(sumSq == 25, "sumSquares(3, 4) should be 9 + 16 = 25")
    var fact5 int = factorial(5)
    println("    factorial(5) =", fact5)
    assert(fact5 == 120, "factorial(5) should be 120")
    var fact7 int = factorial(7)
    println("    factorial(7) =", fact7)
    assert(fact7 == 5040, "factorial(7) should be 5040")
    println("")

    println("[PASSED] All function tests passed")
    println("")

    // =====================================================
    // SECTION A2: Lambda and First-Class Function Tests
    // =====================================================
    println("[SECTION A2] Lambda and First-Class Function Tests")
    println("")

    println("A2.1. Basic lambda expressions")
    var add_lambda func(int, int) int = func(a int, b int) int { a + b }
    var lambda_result1 int = add_lambda(5, 3)
    println("    add_lambda(5, 3) =", lambda_result1)
    assert(lambda_result1 == 8, "Basic lambda add should return 8")
    println("")

    println("A2.2. Lambda as function parameter")
    var apply_result1 int = apply_operation(5, 3, add_lambda)
    println("    apply_operation(5, 3, add_lambda) =", apply_result1)
    assert(apply_result1 == 8, "Lambda as parameter should return 8")
    var sub_lambda func(int, int) int = func(a int, b int) int { a - b }
    var apply_result2 int = apply_operation(10, 4, sub_lambda)
    println("    apply_operation(10, 4, sub_lambda) =", apply_result2)
    assert(apply_result2 == 6, "Lambda subtraction as parameter should return 6")
    println("")

    println("A2.3. Lambda as return value")
    var mul_func func(int, int) int = get_multiplier()
    var lambda_result2 int = mul_func(5, 3)
    println("    get_multiplier()(5, 3) =", lambda_result2)
    assert(lambda_result2 == 15, "Returned lambda multiplier should return 15")
    var div_func func(int, int) int = get_divider()
    var lambda_result3 int = div_func(15, 3)
    println("    get_divider()(15, 3) =", lambda_result3)
    assert(lambda_result3 == 5, "Returned lambda divider should return 5")
    println("")

    println("A2.4. Single parameter lambda")
    var square_lambda func(int) int = func(x int) int { x * x }
    var lambda_result4 int = square_lambda(5)
    println("    square_lambda(5) =", lambda_result4)
    assert(lambda_result4 == 25, "Single parameter lambda square(5) should be 25")
    var lambda_result5 int = apply_unary(7, square_lambda)
    println("    apply_unary(7, square_lambda) =", lambda_result5)
    assert(lambda_result5 == 49, "Unary lambda square(7) should be 49")
    println("")

    println("A2.5. Higher-order functions (chained calls)")
    var get_adder func() func(int, int) int = func() func(int, int) int {
        func(a int, b int) int { a + b }
    }
    var adder func(int, int) int = get_adder()
    var lambda_result6 int = adder(10, 20)
    println("    get_adder()(10, 20) =", lambda_result6)
    assert(lambda_result6 == 30, "Higher-order function should return 30")
    println("")

    println("A2.6. Lambda with no parameters")
    var get_constant func() int = func() int { 42 }
    var lambda_result7 int = get_constant()
    println("    get_constant() =", lambda_result7)
    assert(lambda_result7 == 42, "No-parameter lambda should return 42")
    println("")

    println("[PASSED] All lambda and first-class function tests passed")
    println("")

    // =====================================================
    // SECTION B: Slice Tests
    // =====================================================
    println("[SECTION B] Slice Tests")
    println("")

    println("B1. Vec Creation")
    var nums Vec<int> = Vec<int>{10, 20, 30, 40, 50}
    println("    Created: Vec<int>{10, 20, 30, 40, 50}")
    println("    Length:", nums.len(), "Capacity:", nums.cap())
    assert(nums.len() == 5, "Vec length should be 5")
    assert(nums.cap() == 5, "Vec capacity should be 5")
    var floats Vec<float32> = Vec<float32>{1.1, 2.2, 3.3}
    println("    Created: Vec<float32>{1.1, 2.2, 3.3}")
    println("    Length:", floats.len(), "Capacity:", floats.cap())
    assert(floats.len() == 3, "Float vec length should be 3")
    var empty Vec<int> = Vec<int>{}
    println("    Empty vec - Length:", empty.len(), "Capacity:", empty.cap())
    assert(empty.len() == 0, "Empty vec length should be 0")
    println("")

    println("B2. Vec Element Access")
    var nums2 Vec<int> = Vec<int>{10, 20, 30, 40, 50}
    println("    nums2[0] =", nums2[0])
    assert(nums2[0] == 10, "nums2[0] should be 10")
    println("    nums2[2] =", nums2[2])
    assert(nums2[2] == 30, "nums2[2] should be 30")
    println("    nums2[4] =", nums2[4])
    assert(nums2[4] == 50, "nums2[4] should be 50")
    println("")

    println("B3. Slice Element Modification")
    var nums3 Vec<int> = Vec<int>{10, 20, 30}
    println("    Before: nums3[1] =", nums3[1])
    assert(nums3[1] == 20, "Before modification nums3[1] should be 20")
    nums3[1] = 200
    println("    After:  nums3[1] =", nums3[1])
    assert(nums3[1] == 200, "After modification nums3[1] should be 200")
    println("")

    println("B4. len() and cap() functions")
    var vec1 Vec<int> = Vec<int>{1, 2, 3, 4, 5}
    println("    Vec<int>{1, 2, 3, 4, 5}")
    println("    vec1.len() =", vec1.len())
    assert(vec1.len() == 5, "vec1 length should be 5")
    println("    vec1.cap() =", vec1.cap())
    assert(vec1.cap() == 5, "vec1 capacity should be 5")
    println("")

    println("B5. Slice append - Method syntax")
    var nums5 Vec<int> = Vec<int>{10, 20, 30}
    println("    Initial: Length:", nums5.len())
    assert(nums5.len() == 3, "Initial length should be 3")
    nums5.append(40)
    println("    After nums5.append(40)")
    println("    Length:", nums5.len(), "Capacity:", nums5.cap())
    assert(nums5.len() == 4, "After append length should be 4")
    println("    nums5[3] =", nums5[3])
    assert(nums5[3] == 40, "nums5[3] should be 40")
    println("")

    println("B6. Slice append - Method syntax")
    var nums6 Vec<int> = Vec<int>{1, 2, 3}
    println("    Initial: Length:", nums6.len())
    assert(nums6.len() == 3, "Initial nums6 length should be 3")
    nums6.push(4)
    println("    After nums6.push(4)")
    println("    Length:", nums6.len(), "Capacity:", nums6.cap())
    assert(nums6.len() == 4, "After push(4) length should be 4")
    println("    nums6[3] =", nums6[3])
    assert(nums6[3] == 4, "nums6[3] should be 4")
    nums6.push(5)
    nums6.push(6)
    println("    After more pushes: Length:", nums6.len())
    assert(nums6.len() == 6, "After more pushes length should be 6")
    println("    Elements:", nums6[0], nums6[1], nums6[2], nums6[3], nums6[4], nums6[5])
    assert(nums6[0] == 1, "nums6[0] should be 1")
    assert(nums6[5] == 6, "nums6[5] should be 6")
    println("")

    println("B7. Dynamic capacity growth")
    var growth Vec<int> = Vec<int>{1}
    println("    Initial capacity:", growth.cap())
    assert(growth.cap() == 1, "Initial capacity should be 1")
    growth.push(2)
    println("    After 1 push: Capacity:", growth.cap())
    assert(growth.cap() == 2, "After 1 push capacity should be 2")
    growth.push(3)
    println("    After 2 pushes: Capacity:", growth.cap())
    assert(growth.cap() == 4, "After 2 pushes capacity should be 4")
    growth.push(4)
    growth.push(5)
    println("    After 4 pushes: Capacity:", growth.cap())
    assert(growth.cap() == 8, "After 4 pushes capacity should be 8")
    println("    Length:", growth.len())
    assert(growth.len() == 5, "Final length should be 5")
    println("")

    println("B8. Comprehensive integration")
    var data Vec<int> = Vec<int>{1, 2, 3}
    println("    Created: Vec<int>{1, 2, 3}")
    assert(data.len() == 3, "Initial data length should be 3")
    data[1] = 20
    println("    Modified data[1] = 20")
    assert(data[1] == 20, "data[1] should be 20 after modification")
    data.push(4)
    data.push(5)
    data.push(6)
    println("    Mixed append syntax")
    println("    Final length:", data.len())
    assert(data.len() == 6, "Final length should be 6")
    println("    Elements:", data[0], data[1], data[2], data[3], data[4], data[5])
    assert(data[0] == 1, "data[0] should be 1")
    assert(data[1] == 20, "data[1] should be 20")
    assert(data[2] == 3, "data[2] should be 3")
    assert(data[5] == 6, "data[5] should be 6")
    println("")

    println("[PASSED] All slice tests passed")
    println("")

    // =====================================================
    // Final Summary
    // =====================================================
    println("========================================")
    println("ALL TESTS PASSED!")
    println("========================================")
    println("")
    println("Tested Features:")
    println("  [OK] Functions (void, return, params, references)")
    println("  [OK] Lambda expressions and first-class functions")
    println("  [OK] Higher-order functions (functions as params/return)")
    println("  [OK] Conditional logic (if/else)")
    println("  [OK] Loops (for)")
    println("  [OK] Reference operations")
    println("  [OK] Slice creation and operations")
    println("  [OK] Slice indexing and assignment")
    println("  [OK] Built-in functions (len, cap)")
    println("  [OK] Method call syntax (slice.append, slice.push)")
    println("  [OK] Dynamic memory growth")
    println("")
    println("NRC Language implementation is complete!")
}
